﻿//*****************************************************
//* File: MainForm.cs
//* Author: Уваров А.Ф.
//* Date: 18.08.2023
//* Time:  12:51
//* Version: 1.3
//* Notes: Основная форма приложения RemoteRun
//*****************************************************

//#define DEBUG

using CompAccount;
using Func;
using IPC;
using Message;
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace RemoteRun {

  // Определения класса основной формы MainForm приложения RemoteRun
  public partial class MainForm : Form {
    // возврат каретки, перевод строки
    private const string CRLF = "\r\n";
    // сообщение WM_CLOSE - завершение приложения 
    private const int WM_CLOSE = 0x0010;

    // объект для взаимодействия
    private static ServerNamedPipe SH;
    // данные удаленного сервера
    private DescrServ DS;
    // флаг работы в командном режиме
    private bool FlagCommand = false;
    // флаг запуска удаленного приложения
    private bool FlagRun = false;
    // флаг соединения с удаленным сервером
    private bool FlagConnect = false;
    // флаг открытия потоков приема и передачи данных
    private bool FlagOpen = false;
    // флаг работы с сообщениями формата CSV
    private bool FormatCSV = true;
    // путь загруженной программы
    private string PathApp;

    // индекс выбранного сервера из списка
    private int IndexHost;
    // массив передаваемых параметров
    private string[] Param;
    private BufComm Buffer = new BufComm();
    // имя запускаемого скрипта
#if DEBUG
    private const string NameScript = "RemoteRun.alf";
#else
    private const string NameScript = "RemoteRun.alfc";
#endif

    // *************************************************************
    // * Function: MainForm 
    // * Notes: Конструктор класса, инициализация компонентов основной формы
    // *

    public MainForm() {
      InitializeComponent();
      DS = new DescrServ(this.tbCommands, "RemoteRun v1.4 UAF");
      // получить кодировки
      Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
      CreateObjInter();
      this.tbCommands.ReadOnly = true;
      // скрыть кнопки "Сохранить" и "Отмена"
      this.btSave.Visible = false;
      this.btCancel.Visible = false;
    }

    // *** End of MainForm ***      

    // *************************************************************
    // * Function: MainForm_Shown 
    // * Notes: Обработчик отображения формы
    // *

    private void MainForm_Shown(object sender, EventArgs e) {
      // определение пути загруженной программы
#if DEBUG
      PathApp = @"D:\User\C#\RemoteRun\NET8\RemoteRun_Pipe";
#else
      PathApp = App.GetPath("RemoteRun");
#endif
      Buffer.LoadHistory(PathApp + @"\history.txt");
      if (!DS.ReadFile(PathApp + @"\computer.txt")) {
        this.btServer_Click(null, null);
        if (DS.Status != DescrServ.DataStatus.ErrFormat) {
          DS.IniText();
        }
      }
      if (DS.Count > 0) {
        for (int i = 0; i < DS.Count; i++) {
          this.cbServer.Items.Add(DS.GetName(i));
        }
        IndexHost = 0;
        this.cbServer.Text = DS.GetName(0);
      }
      else {
        IndexHost = -1;
        this.cbServer.Text = "";
        this.tbCommands.Focus();
      }
    }

    // *** End of MainForm_Shown ***      

    // *************************************************************
    // * Function: CreateObjInter 
    // * Notes: Создание объекта взаимодействия с удаленным приложением
    // *

    private void CreateObjInter() {
      try {
        // создание объекта для взаимодействия с удаленным приложением
        SH = new ServerNamedPipe(FormatCSV, false, false);
        // установка обработчика события приема сообщения
        SH.OnEventInputMessage += OnInputMessage;
      }
      catch (Exception Err) {
        MessageBox.Show("Приложение RemoteRun: " + Err,
                        "Ошибка",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
        Environment.Exit(1);
      }
    }

    // *** End of CreateObjInter ***

    // *************************************************************
    // * Function: AddQuotes 
    // * Notes: Обрамление строки в двойные кавычки
    // *

    private string AddQuotes(string path) {
      return !string.IsNullOrWhiteSpace(path) ?
          path.Contains(" ") && (!path.StartsWith("\"") && !path.EndsWith("\"")) ?
              "\"" + path + "\"" : path :
              string.Empty;
    }

    // *** End of AddQuotes ***      

    // *************************************************************
    // * Function: SendCommand 
    // * Notes: Отправить команду удаленному объекту на выполнение
    // *

    private void SendCommand(string Command = "") {
      if (string.IsNullOrEmpty(Command)) {
        Command = this.tbComm.Text;
      }
      if (Command.ToLower() == "quit" ||
        Command.ToLower() == "q" ||
        Command.ToLower() == "close") {
        Command = "close";
        FlagCommand = false;
      }
      // отсылаем команду
      if (FormatCSV)
        SH.Csv.SendMessCommand(Command);
      else
        SH.Json.SendMessCommand(Command);
      this.Activate();
      this.tbComm.Focus();
    }

    // *** End of SendCommand ***      

    // *************************************************************
    // * Function: SetCursor 
    // * Notes: Установка курсора в текстовом поле "Команда"
    // *

    private void SetCursor() {
      this.Activate();
      this.tbComm.Focus();
      this.tbComm.SelectionStart = tbComm.Text.Length;
      this.tbComm.SelectionLength = 0;
    }

    // *** End of SetCursor ***      

    // *************************************************************
    // * Function: MessageOutput 
    // * Notes: Вывод сообщений в окно выполнения команд
    // *

    private void MessageOutput(string Mess) {
      //      tbCommands.AppendText(Mess + CsvRpc.CRLF);

      Action action = () => this.tbCommands.AppendText(Mess + CsvRpc.CRLF);
      try {
        if (this.tbCommands.InvokeRequired) {
          this.tbCommands.Invoke(action);
          this.tbCommands.Refresh();
        }
        else {
          action();
          this.tbCommands.Refresh();
        }
      }
      catch (Exception) {
        return;
      }
    }

    // *** End of MessageOutput ***

    // *************************************************************
    // * Function: ComponentMapping 
    // * Notes: Отображение компонентов основной формы приложения
    // *

    private void ComponentMapping(bool FlagEnable) {
      if (FlagEnable) {
        this.Cursor = Cursors.Default;
        this.tbCommands.Cursor = Cursors.Default;
      }
      else {
        this.Cursor = Cursors.WaitCursor;
        this.tbCommands.Cursor = Cursors.WaitCursor;
      }
      if (FlagCommand) {
        this.lbServer.Enabled = false;
        this.cbServer.Enabled = false;
        this.lbTimeout.Enabled = false;
        this.mtbTimeout.Enabled = false;
        this.lbPort.Enabled = false;
        this.mtbPort.Enabled = false;
        this.lbFile.Enabled = false;
        this.tbNameFile.Enabled = false;
        this.btSelectFile.Enabled = false;
        this.lbComm.Enabled = FlagEnable;
        this.tbComm.Enabled = FlagEnable;
        this.btRun.Enabled = FlagEnable;
        this.Activate();
        this.tbComm.SelectAll();
        this.tbComm.Focus();
      }
      else {
        this.lbServer.Enabled = FlagEnable;
        this.cbServer.Enabled = FlagEnable;
        this.lbTimeout.Enabled = FlagEnable;
        this.mtbTimeout.Enabled = FlagEnable;
        this.lbPort.Enabled = FlagEnable;
        this.mtbPort.Enabled = FlagEnable;
        this.lbFile.Enabled = FlagEnable;
        this.tbNameFile.Enabled = FlagEnable;
        this.btSelectFile.Enabled = FlagEnable;
        this.lbComm.Enabled = FlagEnable;
        this.tbComm.Enabled = FlagEnable;
        this.btServer.Enabled = FlagEnable;
        this.btRun.Enabled = FlagEnable;
        this.Activate();
        this.tbNameFile.Focus();
      }
    }

    // *** End of ComponentMapping ***      

    // *************************************************************
    // * Function: AppRun 
    // * Notes: Запуск Alfa приложения 
    // *

    private void AppRun(string CommandStr, string NameApp,
                           EventHandler OnExit, out bool FlagRunProc,
                           int Timeout = 0) {
      bool Flag = false;

      FlagRunProc = false;
      // запустить скрипт
      UserProcess UserProc = new UserProcess();
      if (OnExit != null) {
        UserProc.OnExit = OnExit;
        UserProc.ObjProc.SynchronizingObject = this;
      }
      try {
        Flag = UserProc.RunAlfaEx(CommandStr, "", Timeout, true);
        if (Flag)
          FlagRunProc = true;
      }
      catch (Exception Err) {
        if (FormatCSV)
          SH.Csv.SendMessError("Приложение " + NameApp + ": " + Err.Message);
        else
          SH.Json.SendMessError("Приложение " + NameApp + ": " + Err.Message);
        MessageBox.Show("Приложение " + NameApp + ": " + Err.Message,
                        "Ошибка",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
        FlagRunProc = false;
        return;
      }
    }

    // *** End of AppRun ***      

    // *************************************************************
    // * Function: OnExitAlfa 
    // * Notes: Обработчик завершения удаленного процесса приложения Alfa 
    // *

    private void OnExitAlfa(Object source, EventArgs e) {
      if (this.FlagCommand) {
        this.tbComm.Text = "   ";
        this.tbComm.Text = "";
      }
      else {
        this.tbNameFile.Text = "";
      }
      this.FlagCommand = false;
      FlagRun = false;
      ComponentMapping(true);
    }

    // *** End of OnExitAlfa ***

    // *************************************************************
    // * Function: btSelectFile_Click 
    // * Notes: Обработчик нажатия кнопки "Выбор файла" 
    // *
    private void btSelectFile_Click(object sender, EventArgs e) {
      if (this.OpenFileDialog.ShowDialog() == DialogResult.Cancel) {
        this.tbNameFile.Text = "";
        this.lbComm.Enabled = true;
        this.tbComm.Enabled = true;
        return;
      }
      this.tbNameFile.Text = this.OpenFileDialog.FileName;
      this.lbComm.Enabled = false;
      this.tbComm.Enabled = false;
    }

    // *** End of btSelectFile_Click ***      

    // *************************************************************
    // * Function: btExit_Click 
    // * Notes: Обработчик нажатия кнопки "Выход"
    // *

    private void btExit_Click(object sender, EventArgs e) {
      if (FlagRun) {
        if (FormatCSV)
          SH.Csv.SendMessCommand("close");
        else
          SH.Json.SendMessCommand("close");
        Thread.Sleep(200);
      }
      SH.Close();
      FlagOpen = false;
      Buffer.SaveHistory(PathApp + @"\history.txt");
      Environment.Exit(0);
    }

    // *** End of btExit_Click ***

    // *************************************************************
    // * Function: OnInputMessage 
    // * Notes: Обработчик события приема сообщений
    // *

    private void OnInputMessage(object sender, EventArgs e) {
      // массив значений входных данных
      Object[] Values = null;
      // массив имен входных данных
      string[] Names = null;
      // данные сообщения
      string DataMess;
      // тип сообщения
      string TypeMess;
      // сообщение
      string Mess;

      if (SH == null || SH.CountInMessage == 0)
        return;
      Mess = SH.GetInMessage();
      if (Mess == "")
        return;
      // получить данные сообщения
      if (FormatCSV)
        SH.Csv.ParseMess(Mess, out Names, out Values);
      else
        SH.Json.ParseMess(Mess, out Names, out Values);
      // определяем тип сообщения
      TypeMess = (string)Values[0];
      // получить данные сообщения
      DataMess = (string)Values[1];
      // обработать данные сообщения
      switch (TypeMess) {
        // информационное сообщение
        case DataRpc.INFO_MESS: {
          MessageOutput(DataMess);
          break;
        }
        // результат выполнения команды
        case DataRpc.COMM_RESULT: {
          MessageOutput(DataMess);
          break;
        }
        // сообщение об ошибке
        case DataRpc.ERR_MESS: {
          MessageOutput(DataMess);
          if (FlagCommand) {
            if (FormatCSV)
              SH.Csv.SendMessCommand("close");
            else
              SH.Json.SendMessCommand("close");
          }
          break;
        }
      }
    }

    // *** End of OnInputMessage ***      

    // *************************************************************
    // * Function: btRun_Click 
    // * Notes: Обработчик нажатия кнопки "Выполнить"
    // *
    private void btRun_Click(object sender, EventArgs e) {
#if DEBUG
      string PathScript = PathApp + @"\Scripts";
      string PathAssembly = PathApp + @"\RemoteRun";
#else
      string PathScript = PathApp + @"\Scripts";
      string PathAssembly = PathApp;
#endif

      string Port = "";
      string NameFile = "";
      string Command = "";
      string ArgApp = "";
      int Timeout;
      int ErrCode;

      // зафиксирована ошибка приложения Alfa при
      // предыдущем выполнении btRun_Click()
      if (FormatCSV)
        ErrCode = SH.Csv.ErrCode;
      else
        ErrCode = SH.Json.ErrCode;
      if (ErrCode > 0 || !FlagRun) {
        // закрываем все потоки и освобождаем занятые ресурсы
        SH.Close();
        // сбрасываем флаг
        FlagOpen = false;
        // пересоздаем объект для взаимодействия с удаленным приложением Alfa
        CreateObjInter();
      }
      if (!FlagOpen) {
        // открываем все потоки приема-передачи сообщенний
        SH.Open();
        // устанавливаем флаг
        FlagOpen = true;
      }
      PathScript = AddQuotes(PathScript + @"\" + NameScript);
      PathAssembly = AddQuotes(PathAssembly);
      ArgApp = PathScript + " -c " + PathAssembly;

      this.tbCommands.Clear();
      IndexHost = this.cbServer.SelectedIndex;
      Param = DS.GetArrParam(IndexHost);
      if (Param == null) {
        MessageBox.Show("Приложение " + DS.NameApp + ": " +
                        "Нет данных в поле 'Сервер'",
                        "Ошибка",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
        return;
      }
      try {
        if (this.mtbPort.Text != "")
          Port = this.mtbPort.Text;
        else
          Port = "22";
        if (this.mtbTimeout.Text != "")
          Timeout = Convert.ToInt32(this.mtbTimeout.Text);
        else
          Timeout = 0;
        if (this.tbNameFile.Text == "" && this.tbComm.Text == "") {
          MessageBox.Show("Приложение " + DS.NameApp + ": " +
                          "Должно быть заполнено поле 'Файл' или 'Команда'",
                          "Ошибка",
                          MessageBoxButtons.OK,
                          MessageBoxIcon.Error);
          return;
        }
        else if (this.tbNameFile.Text != "" && this.tbNameFile.Enabled) {
          FlagCommand = false;
        }
        else if (this.tbComm.Text != "") {
          FlagCommand = true;
        }
        // файла пакета команд
        if (this.cbServer.Text != "" && this.tbNameFile.Text != "" && !FlagCommand)
          NameFile = this.tbNameFile.Text;
        else
          NameFile = "";
        // запускаем приложение RemoteRun не ожидая завершения
        if (!FlagRun) {
          try {
            AppRun(ArgApp, PathScript, OnExitAlfa, out FlagRun, 0);
            // устанавливаем флаг запуска приложения
            FlagRun = true;
          }
          catch (Exception Exc) {
            MessageBox.Show("Процес RemoteRun: " +
                            "Ошибка запуска - " + Exc.Message,
                            "Ошибка",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error);
            FlagRun = false;
            return;
          }
        }
        // осуществляем соединение с удаленным сервером
        if (!FlagConnect) {
          // отправляем команду на соединение с удаленным сервером
          if (FormatCSV)
            SH.Csv.SendMessConnect(Param[DescrServ.IP], Param[DescrServ.USER],
                                   Param[DescrServ.PASSWORD], Convert.ToInt32(Port),
                                   Param[DescrServ.OS]);
          else
            SH.Json.SendMessConnect(Param[DescrServ.IP], Param[DescrServ.USER],
                                    Param[DescrServ.PASSWORD], Convert.ToInt32(Port),
                                    Param[DescrServ.OS]);
          FlagRun = true;
          FlagConnect = true;
        }
        // отправляем на выполнение пакета команд
        if (NameFile != "") {
          if (FormatCSV)
            SH.Csv.SendMessPackage(NameFile);
          else
            SH.Json.SendMessPackage(NameFile);
          FlagRun = false;
          FlagConnect = false;
          FlagCommand = false;
        }
        // обработка команды
        if (this.cbServer.Text != "" && FlagCommand) {
          Command = this.tbComm.Text;
          if (Command.ToLower() == "q" ||
              Command.ToLower() == "quit" ||
              Command.ToLower() == "close") {
            Command = "close";
            this.tbComm.Text = "";
            FlagRun = false;
            FlagConnect = false;
            FlagCommand = false;
          }
          Buffer.WriteComm(Command);
          Buffer.Reset();
          // отправляем команду на выполнение
          if (FormatCSV)
            SH.Csv.SendMessCommand(Command);
          else
            SH.Json.SendMessCommand(Command);
        }
      }
      finally {
        ComponentMapping(true);
      }
    }

    // *** End of btRun_Click ***

    // *************************************************************
    // * Function: tbComm_TextChanged 
    // * Notes: Обработчик изменения текста в текстовом поле "Команда"
    // *

    private void tbComm_TextChanged(object sender, EventArgs e) {
      if (string.IsNullOrEmpty(this.tbComm.Text)) {
        if (!(this.tbNameFile.Enabled) && !FlagRun) {
          this.lbFile.Enabled = true;
          this.tbNameFile.Enabled = true;
          this.btSelectFile.Enabled = true;
          this.lbTimeout.Enabled = true;
          this.mtbTimeout.Enabled = true;
        }
      }
      else {
        if (this.tbNameFile.Enabled) {
          this.lbFile.Enabled = false;
          this.tbNameFile.Enabled = false;
          this.btSelectFile.Enabled = false;
          this.lbTimeout.Enabled = false;
          this.mtbTimeout.Enabled = false;
        }
      }
    }

    // *** End of tbComm_TextChanged ***      

    // *************************************************************
    // * Function: tbComm_KeyPress 
    // * Notes: Избавление от звукового сигнала при нажатии на клавишу Enter 
    // *

    private void tbComm_KeyPress(object sender, KeyPressEventArgs e) {
      if (e.KeyChar == (char)13)
        e.Handled = true;
      else
        base.OnKeyPress(e);
    }

    // *** End of tbComm_KeyPress ***      

    // *************************************************************
    // * Function: tbComm_PreviewKeyDowne 
    // * Notes: Обработчик нажатия клавиши "Enter" в текстовом поле "Команда"
    // *

    private void tbComm_PreviewKeyDown(object sender,
                                        PreviewKeyDownEventArgs e) {
      if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return) {
        this.btRun.PerformClick();
      }
    }

    // *** End of tbComm_PreviewKeyDowne ***      

    // *************************************************************
    // * Function: void tbComm_KeyDown 
    // * Notes: Обработчик нажатия клавиши "Стрелка вверх" и
    // *        клавиши "Стрелка вниз" в текстовом поле "Команда"
    // *

    private void tbComm_KeyDown(object sender, KeyEventArgs e) {
      if (e.KeyCode == Keys.Up) {
        this.tbComm.Text = Buffer.PrevComm();
        SetCursor();
        e.Handled = true;
      }
      else if (e.KeyCode == Keys.Down) {
        this.tbComm.Text = Buffer.NextComm();
        SetCursor();
        e.Handled = true;
      }
    }

    // *** End of void tbComm_KeyDown ***      

    // *************************************************************
    // * Function: Form1_FormClosedName 
    // * Notes: Обработчик закрытия основной формы приложения
    // *

    private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
      this.btExit.PerformClick();
    }

    // *** End of Form1_FormClosedName ***      

    // *************************************************************
    // * Function: tbNameFile_PreviewKeyDown 
    // * Notes: Обработчик нажатия клавиши "Enter" в текстовом поле "Файл" 
    // *

    private void tbNameFile_PreviewKeyDown(object sender,
                                            PreviewKeyDownEventArgs e) {
      if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return) {
        this.btRun.PerformClick();
      }
    }

    // *** End of tbNameFile_PreviewKeyDown ***      

    // *************************************************************
    // * Function: tbNameFile_TextChanged 
    // * Notes: Обработчик изменения текста в текстовом поле "Файл"
    // *

    private void tbNameFile_TextChanged(object sender, EventArgs e) {
      if (string.IsNullOrEmpty(this.tbNameFile.Text)) {
        if (!(this.tbComm.Enabled) && !FlagRun) {
          this.lbComm.Enabled = true;
          this.tbComm.Enabled = true;
        }
      }
      else {
        if (this.tbComm.Enabled) {
          this.lbComm.Enabled = false;
          this.tbComm.Enabled = false;
        }
      }
    }

    private void tbNameFile_KeyPress(object sender, KeyPressEventArgs e) {
      if (e.KeyChar == (char)13)
        e.Handled = true;
      else
        base.OnKeyPress(e);
    }

    // *** End of tbNameFile_TextChanged ***      

    // *************************************************************
    // * Function: btServer_Click 
    // * Notes: Обработчик кнопки "Сервер" 
    // *

    private void btServer_Click(object sender, EventArgs e) {
      // скрыть кнопки "Сервер", "Выполнить", "Выход"
      this.btServer.Visible = false;
      this.btRun.Visible = false;
      this.btExit.Visible = false;
      // показать кнопки "Сохранить", "Отмена"
      this.btSave.Visible = true;
      this.btCancel.Visible = true;
      // перевести компонент tbCommands в режим редактирования
      this.tbCommands.ReadOnly = false;
      this.tbCommands.Focus();
      string NameFile = PathApp + @"\computer.txt";
      this.DS.LoadTextBox(NameFile);
      //      VisibleButton(true);
    }

    // *** End of btServer_Click ***      

    // *************************************************************
    // * Function: btSave_Click 
    // * Notes: Обработчик кнопки "Сохранить"
    // *

    private void btSave_Click(object sender, EventArgs e) {
      string NameFile = PathApp + @"\computer.txt";
      this.DS.SaveTextBox(NameFile);
      cbServer.Items.Clear();
      if (DS.Count > 0) {
        for (int i = 0; i < DS.Count; i++) {
          cbServer.Items.Add(DS.GetName(i));
        }
        IndexHost = 0;
        cbServer.Text = DS.GetName(0);
      }
      else {
        IndexHost = -1;
        cbServer.Text = "";
      }
      VisibleButton(true);
      return;
    }

    // *** End of btSave_Click ***      

    // *************************************************************
    // * Function: btCancel_Click 
    // * Notes: Обработчик кнопки "Отмена" 
    // *

    private void btCancel_Click(object sender, EventArgs e) {
      VisibleButton(true);
    }

    // *** End of btCancel_Click ***      

    // *************************************************************
    // * Function: cbServer_SelectionChangeCommitted 
    // * Notes: Обработчик выпадающего списка "Сервер"
    // *        (происходит, когда выбранный элемент изменился)
    // *

    private void cbServer_SelectionChangeCommitted(object sender,
                                                    EventArgs e) {
      IndexHost = this.cbServer.SelectedIndex;
      Param = DS.GetArrParam(IndexHost);
      this.cbServer.Text = Param[DescrServ.NAME];
    }

    // *** End of cbServer_SelectionChangeCommitted ***      

    // *************************************************************
    // * Function: VisibleButton 
    // * Notes: Показать/скрыть кнопки основной формы 
    // *

    private void VisibleButton(bool FlagVisible) {
      this.tbCommands.Clear();
      // показать кнопки "Сервер", "Выполнить", "Выход"
      this.btServer.Visible = FlagVisible;
      this.btRun.Visible = FlagVisible;
      this.btExit.Visible = FlagVisible;
      this.tbCommands.ReadOnly = FlagVisible;
      // скрыть кнопки "Сохранить", "Отмена"
      this.btSave.Visible = !FlagVisible;
      this.btCancel.Visible = !FlagVisible;
    }

    // *** End of VisibleButton ***      

    // *************************************************************
    // * Function: WndProc 
    // * Notes: Обработчик сообщения WM_CLOSE - завершение приложения
    // *

    protected override void WndProc(ref System.Windows.Forms.Message m) {
      if (m.Msg == WM_CLOSE) {
        this.btExit.PerformClick();
      }
      base.WndProc(ref m);
    }

    // *** End of WndProc ***      

    // Определение класса кольцевого буфера команд
    // -------------------------------------------
    public class BufComm {
      // кодовая страница файла истории команд (UTF-8)
      public const int CODE_PAGE = 65001;
      // длина буфера команд
      private const int LenBuf = 20;
      // индекс записи команда
      private int IndexWrite = 0;
      // индекс чтения команды
      private int IndexRead = 0;
      // буфер команд
      private string[] Buf = new string[LenBuf];
      // флаг наличия данных
      private bool FlagData = false;


      // *************************************************************
      // * Function: PrevComm 
      // * Notes: Возвращает предыдущую команду из кольцевого буфера команд
      // *

      public string PrevComm() {
        --IndexRead;
        if (IndexRead < 0) {
          IndexRead = LenBuf - 1;
          if (string.IsNullOrEmpty(Buf[IndexRead])) {
            ++IndexRead;
            if (IndexRead > LenBuf - 1)
              IndexRead = 0;
          }
        }
        else if (string.IsNullOrEmpty(Buf[IndexRead])) {
          ++IndexRead;
        }
        return Buf[IndexRead];
      }

      // *** End of PrevComm ***      

      // *************************************************************
      // * Function: NextComm 
      // * Notes: Возвращает следующую команду из кольцевого буфера команд 
      // *

      public string NextComm() {
        ++IndexRead;
        if (IndexRead > LenBuf - 1) {
          IndexRead = 0;
          if (string.IsNullOrEmpty(Buf[IndexRead])) {
            --IndexRead;
            if (IndexRead < 0)
              IndexRead = LenBuf - 1;
          }
        }
        else if (string.IsNullOrEmpty(Buf[IndexRead])) {
          --IndexRead;
        }
        return Buf[IndexRead];
      }

      // *** End of NextComm ***      

      // *************************************************************
      // * Function: WriteComm 
      // * Notes: Записывает текущую команду в кольцевой буфер команд
      // *

      public void WriteComm(string Data) {
        Buf[IndexWrite] = Data;
        if (IndexWrite == LenBuf - 1)
          IndexWrite = 0;
        else
          IndexWrite++;
        if (!FlagData)
          FlagData = true;
      }

      // *** End of WriteComm ***      

      // *************************************************************
      // * Procedure: Reset 
      // * Notes: Устанавливает индекс чтения команд в начальное положение 
      // *

      public void Reset() {
        IndexRead = IndexWrite;
      }

      // *** End of Reset ***      

      // *************************************************************
      // * Function: IsData 
      // * Notes: Возвращает флаг наличия данных в буфере
      // *

      public bool IsData() {
        return FlagData;
      }

      // *** End of IsData ***      

      // *************************************************************
      // * Procedure: LoadHistory 
      // * Notes: Загружает данные файла истории выполнения команд
      // *

      public void LoadHistory(string NameFile) {
        int Num = 0;

        // чтение файла и загрузка содержимого в буфер выполненых команд
        if (File.Exists(NameFile)) {
          // объект файлового потока для чтения
          StreamReader oFR = new StreamReader(NameFile, Encoding.GetEncoding(CODE_PAGE));
          while (!oFR.EndOfStream) {
            if (Num < LenBuf)
              Buf[Num++] = oFR.ReadLine();
            else
              break;
          }
          oFR.Close();
        }
      }

      // *** End of LoadHistory ***      

      // *************************************************************
      // * Procedure: SaveHistory 
      // * Notes: Сохраняет выполненные команды в файле 
      // *

      public void SaveHistory(string NameFile) {
        int Num = 0;
        string Str = "";

        // проверяем наличие данных в буфере
        if (!FlagData)
          return;
        // запись буфера выполненых команд в файл
        StreamWriter oFW = new StreamWriter(NameFile, false, Encoding.GetEncoding(CODE_PAGE));
        while (Num < LenBuf) {
          Str = Buf[Num++];
          if (!String.IsNullOrEmpty(Str))
            oFW.WriteLine(Str);
        }
        oFW.Flush();
        oFW.Close();
      }

      // *** End of SaveHistory ***      

    }
  }
}

// *** End of MainForm.cs ***      
